refactor(core): centralize model stream attempt state - #9683
Draft
DragonnZhang wants to merge 5 commits into
Draft
refactor(core): centralize model stream attempt state#9683DragonnZhang wants to merge 5 commits into
DragonnZhang wants to merge 5 commits into
Conversation
DragonnZhang
added a commit
to DragonnZhang/qwen-code
that referenced
this pull request
Aug 23, 2026
Same latent main-side violation as QwenLM#9682/QwenLM#9683-era fixes: QwenLM#9587 grew the workflow without a baseline update; record the new size as the check message directs (precedent QwenLM#9747).
Conflict resolution notes: - agent-core.ts: kept the centralized attempt-state path. Upstream added isContinuation propagation on the legacy inline retry handling; the state machine already covers it — ModelStreamAttemptState.accept() captures the whole retry event (incl. isContinuation) into transition.retryInfo, and the Retry resetEvent built from it carries isContinuation into checkSubagentLoop/LoopDetectionService. - loopDetectionService.ts: adopted upstream's separate Retry / ModelFallback heuristic cases (semantically identical to this PR's grouped case: continuation retries keep content state, replays and fallbacks reset it) and kept this PR's always-on cap-rollback extension to ModelFallback. - agent-core.test.ts / speculation.test.ts: kept both new suites (model stream attempts here; approval-response deduplication and the session-scoped cache-safe-params test upstream). Added getSessionId to the fallback test's config mock for upstream's session-scoped getCacheSafeParams lookup. Verified: tsc --build (packages/core) clean; vitest green for agent-core, speculation, loopDetectionService, model-stream-attempt-state, and turn suites.
…odel-stream-attempt-state
The attempt-state centralization normalized the Retry event emitted in AgentCore to always carry isContinuation (false for fresh retries). Upstream's subagent loop-detection contract (agent-headless.test.ts, 'forwards retry events to subagent loop detection') requires fresh retries to omit the property entirely, matching the pre-refactor conditional forwarding. Restore the original event shape by only spreading isContinuation when the upstream retry event carries it.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this PR does
This PR introduces one shared per-attempt state model for streamed model responses and routes every raw model-stream consumer through it. Text, thoughts, tool calls, usage, response identifiers, finish reasons, and truncation state now follow the same retry, continuation, fallback, and compression semantics across the main turn, subagents, forked queries, speculative execution, and ACP-driven flows.
Fresh retries and model fallbacks discard the failed attempt. Continuation retries preserve accumulated text and thoughts while clearing tool calls and attempt-scoped metadata. Message-display hooks and loop detection now follow the same reset decision.
Why it's needed
Stream consumers previously maintained independent copies of attempt state. Those implementations had drifted: subagents dropped the first half of continuation retries, fallback output could be appended to stale text, ACP background responses retained failed-attempt usage, and stale tool calls or response identifiers could survive a reset. Centralizing the lifecycle removes those inconsistencies and gives future protocol-boundary work one place to preserve retry semantics.
Reviewer Test Plan
How to verify
Evidence (Before & After)
N/A — this is a non-UI stream-lifecycle refactor. Deterministic regression fixtures reproduce the stale-state behavior before the change and pass after the shared attempt state is applied.
Tested on
Environment (optional)
Local macOS workspace with Node.js v24.18.0. Verified with 238 focused core tests, two client integration tests, all 669 ACP Session tests, targeted ACP reset tests, the full repository build, and workspace type checking. Targeted lint for every changed source and test file passed.
Risk & Scope
Linked Issues
N/A
中文说明
本 PR 做了什么
本 PR 为模型流式响应引入了一套共享的单次尝试状态模型,并让所有直接消费原始模型流的路径统一经过该模型。主会话、子 Agent、分叉查询、推测执行和 ACP 流程现在对文本、思考、工具调用、用量、响应标识、结束原因和截断状态采用一致的重试、续写、回退和压缩语义。
全新重试和模型回退会丢弃失败尝试;续写重试会保留已经累积的文本和思考,同时清除工具调用与本次尝试相关的元数据。MessageDisplay hook 和循环检测也遵循同一个重置判断。
为什么需要它
此前各个流消费者分别维护自己的尝试状态,具体实现已经发生偏移:子 Agent 会丢失续写重试的前半段,模型回退输出可能拼接到旧文本后面,ACP 后台响应会保留失败尝试的用量,重置后还可能残留旧工具调用或响应标识。集中管理生命周期可以消除这些不一致,并为未来调整协议边界提供一个统一保留重试语义的位置。
Reviewer 测试计划
如何验证
证据(修改前与修改后)
N/A——这是非 UI 的流生命周期重构。确定性的回归夹具可以在修改前复现状态残留问题,并在应用共享尝试状态后通过。
测试平台
环境(可选)
本地 macOS 工作区,Node.js v24.18.0。已验证 238 个核心定向测试、2 个客户端集成测试、全部 669 个 ACP Session 测试、ACP 重置定向测试、全仓构建和工作区类型检查。本次所有变更源文件与测试文件的定向 lint 均通过。
风险与范围
关联 Issue
N/A